home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 42 / Amiga Format AFCD42 (Issue 126, Aug 1999).iso / -serious- / programming / basic / udpchat / old_stuff / udp_chatv2.2.asc < prev    next >
Encoding:
Text File  |  1999-05-14  |  32.8 KB  |  933 lines

  1. ;
  2. ;                      UDP Chat code V2.2   14/11/98
  3. ;
  4. ;    This code sends and receives UDP data packets like a
  5. ;  a simple IRC client, and  checks wether they have arrived
  6. ;  at their destination, and allows other copies of this
  7. ;  program to log in to this one (can act as a server or client).
  8. ;
  9. ;  Written by Anton Reinauer <anton@ww.co.nz>.
  10. ;  GUI: Alvaro Thompson <alvaro@enterprise.net> - And awful hacks
  11. ;  by me to his nice font sensitive code :-)
  12. ;        - typical bloody games programmer ;-)
  13. ;
  14. ;  Thanks to Paul Burkey for TCP_Funcs and to Dr. Ercole Spiteri
  15. ;  for TCP-to-Blitz.
  16. ;
  17. ;  ARexx portname: "UDP_Chat"
  18. ;
  19. ;  Turn overflow errors off in Debugger options,
  20. ;  and have amigalibs.res resident in compiler options.
  21.  
  22.  
  23. WBStartup
  24. NoCli
  25. Hostname.s="localhost"  ; "localhost" default destination host address
  26. #PORT=27272             ; 27272 default destination port to send data to
  27. #LOCALPORT=27272        ; 27272
  28. #NO_CONNECTION=1        ; 0 - are we connected, or have we been connected to the Internet since bootup?
  29.                         ;   - 1 for not been connected, 0 for have/are connected.
  30. #DEBUG=1                ; 1 - 1 for debug info and multiple local copies
  31.  
  32. #MAX_NUMBER_PLAYERS=8   ; 8
  33. GAME_NAME.s="UDP_Chat"  ; "UDP_Chat"  Game name (for login)
  34. PORT_NAME.s="UDP_Chat"  ; "UDP_Chat"  Rexx portname
  35.  
  36. INCLUDE "Net_Protocol_Header.bb2"  ; Net protocol constants
  37. INCLUDE "UDPHeader.bb2"  ;Standard TCP/UDP library structures
  38.  
  39. DEFTYPE .w
  40.  
  41. packet_number.l=0
  42. online=0
  43. my_number=0
  44. last_message_number.l=0
  45. free_message.l=0
  46. sock.l=-1
  47. ;********************************************************
  48.  
  49. NEWTYPE .message_status
  50.   number.l     ; packet number
  51.   ack.w        ; has it been received yet?
  52.   timestamp.l  ; when was it sent?
  53.   message.s    ; message
  54.   resends.w    ; how many times has it been re-sent?
  55.   player       ; which player was it sent too?
  56. End NEWTYPE
  57.  
  58. NEWTYPE .player_info
  59. status.w              ; 0 offline, 1 online
  60. ascii_host_string.s
  61. player_colour.w
  62. score.l
  63. name.s
  64. nick.s
  65. End NEWTYPE
  66.  
  67. ;*******************************************************
  68. .Dims
  69.  
  70. Dim messages.message_status(100)
  71. Dim host.sockaddrin(#MAX_NUMBER_PLAYERS), hostlen.l(#MAX_NUMBER_PLAYERS)
  72. Dim players.player_info(#MAX_NUMBER_PLAYERS),pingprintx.w(#MAX_NUMBER_PLAYERS)
  73. Dim player_honesty.b(#MAX_NUMBER_PLAYERS,#MAX_NUMBER_PLAYERS)
  74.  
  75. DEFTYPE .sockaddrin temphost
  76.  
  77. INCLUDE "UDPFuncs.bb2"  ; basic UDP socket functions
  78.  
  79. ;*******************************************************
  80.  
  81. .Print_String                ; Print a string in window and scroll if necessary
  82. Statement Print_String{text$}
  83.   SHARED ypos
  84.   If ypos<200
  85.     ypos+10
  86.   Else
  87.     WScroll 10,60,590,220,0,10
  88.   EndIf
  89.  
  90.   WLocate 10,ypos
  91.   Print text$               ; print string received
  92. End Function
  93.  
  94. .Get_Packet_Source            ; Check if packet has come from player already logged on.
  95. Function.w Get_Packet_Source{}
  96.  SHARED host(),temphost
  97.  i=0
  98.  Repeat                    ; check for each player (hostname and socket)
  99.    i+1
  100.    If host(i)\sin_addr\s_addr=temphost\sin_addr\s_addr AND  host(i)\sin_port=temphost\sin_port
  101.      exit=1
  102.    EndIf
  103.    If i=8 AND exit=0       ; if at end of players and no match is found
  104.      i=-1
  105.      exit=1
  106.    EndIf
  107.  Until  exit=1
  108.  
  109.  Function Return i
  110. End Function
  111.  
  112. .Get_Ascii_Address                     ; build a.b.c.d numerical address from long number
  113. Function.s Get_Ascii_Address{address.l}
  114.   string_address.l=Inet_NtoA_(address) ; get memory address of ASCII string version of host address (a.b.c.d)
  115.   If string_address=0
  116.     Function Return ""
  117.   EndIf
  118.   Repeat                               ; build ASCII host string
  119.     letter.b=(Peek.b(string_address))
  120.     If letter<>0
  121.       temp$=temp$+Chr$(letter)
  122.     EndIf
  123.     string_address+1
  124.   Until letter=0
  125.   Function Return temp$
  126. End Function
  127.  
  128. .Send_Reliable_Message         ; this message is checked to see if it has arrived, and resent if not
  129. Statement Send_Reliable_Message{send_string.s,player}       ; if no confirmation can be made, link is considered lost
  130.  SHARED messages(),free_message,last_message_number,packet_number.l
  131.  
  132.   send_string.s=Mkl$(packet_number) + send_string.s
  133.   messages(free_message)\number=packet_number,False,Ticks,send_string.s,0,player  ; log message in message array
  134.   If messages(last_message_number)\ack=True
  135.     last_message_number=free_message
  136.   EndIf
  137.  
  138.   WriteUDP{&send_string.s,Len(send_string.s),player}   ; send message
  139.   packet_number+1               ; set packet number to next free packet number (+1)
  140.   free_message+1
  141.   If free_message=101 Then free_message=0    ; if packet number >100 then wrap back to 0
  142.  
  143. End Statement
  144.  
  145. .Get_Host_By_Name        ; get a host structure by name (if exists), from a name server
  146. Function Get_Host_By_Name{host$,port.w,player}
  147.   SHARED host(),hostlen()
  148.  
  149.   *a.hostent=gethostbyname_(host$)     ; set up destination address to send packets to
  150.   If *a.hostent=0
  151.     WLocate 10,18
  152.     Print "    No connection to host           "
  153.     Function Return False
  154.   Else
  155.     WLocate 10,18
  156.     Print "Type in data, and hit return to send"
  157.   EndIf
  158.  
  159.   ;Copy Details to our Sockaddrin structure
  160.  
  161.   bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(player)\sin_addr,*a.hostent\h_length)
  162.  
  163.   host(player)\sin_port=port       ;Set port number
  164.   host(player)\sin_family=2        ;Set type to AT_INET
  165.   hostlen(player)=SizeOf.sockaddrin        ;Get length of structure sockaddrin
  166.  
  167.   Function Return True
  168. End Function
  169.  
  170. .Get_Host_By_Address      ; get a host structure by address (if exists), from a name server
  171. Function.b Get_Host_By_Address{address.l,port,player}
  172.   SHARED host(),hostlen()
  173.  
  174.   *a.hostent=gethostbyaddr_(address,4,2)  ; check wether host exists- we're not being shammed
  175.  
  176.   If *a.hostent=0
  177.     Function Return False
  178.   EndIf
  179.  
  180.   bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(player)\sin_addr,*a.hostent\h_length) ; copy details to player's host
  181.                                                                                        ; newtype to host
  182.   host(player)\sin_port=port               ;Set port number
  183.   host(player)\sin_family=2                ;Set type to AT_INET
  184.   hostlen(player)=SizeOf.sockaddrin        ;Get length of structure sockaddrin
  185.   Function Return True
  186. End Function
  187.  
  188. .Localhost_Name       ; get our internet address- it changes each time we log in to our ISP
  189. Function.s Localhost_Name{}
  190.     If OpenFile (0,"ENV:HOSTNAME")
  191.        FileInput 0
  192.        While NOT Eof(0)
  193.          a$=a$+Inkey$
  194.        Wend
  195.        CloseFile 0
  196.        WindowInput 0
  197.        Function Return a$
  198.     EndIf
  199.     Function Return "localhost"
  200. End Function
  201.  
  202. .Connect_To_Server
  203. Function Connect_to_Server{host.s,port.w}
  204.   SHARED online, CP_REQ_CONNECT.s, GAME_NAME.s, NET_PROTOCOL_VERSION.s
  205.   If online=0
  206.     If Get_Host_By_Name{GTGetString(0,51), GTGetInteger(0,52),1}=True
  207.       Print_String{"Attempting to connect to Server"}
  208.       send$=Mkl$(0) + CP_REQ_CONNECT.s + GAME_NAME.s + Chr$(0) + NET_PROTOCOL_VERSION.s  ; "0000" is a Pad for packet number
  209.       WriteUDP{&send$,Len(send$),1}   ; send connection request
  210.     Else
  211.       Function Return False
  212.     EndIf
  213.   Else
  214.     If online=2
  215.       Print_String{"Already logged on to a Server!"}
  216.     Else
  217.       Print_String{"Already acting as a Server!"}
  218.     EndIf
  219.     Function Return False
  220.   EndIf
  221. End Function
  222.  
  223. .Resend_Message       ; resend a message
  224. Function Resend_Message{message_number}
  225.   SHARED messages()
  226.   messages(message_number)\timestamp=Ticks
  227.   messages(message_number)\resends+1
  228.   If messages(message_number)\resends>5  ; is host not responding? After 5 retries consider link broken.
  229.      Print_String{"Host not responding to Packet no: " + Str$(message_number)}
  230.      Function Return False
  231.   Else
  232.      WriteUDP{&messages(message_number)\message,Len(messages(message_number)\message),messages(message_number)\player}
  233.      Print_String{"Resent Packet no:" + Str$(message_number)}
  234.      Function Return True
  235.   EndIf
  236. End Statement
  237.  
  238. .Find_Next_Message
  239. Statement Find_Next_Message{}
  240. SHARED last_message_number,messages(),free_message
  241.  
  242.   exit=0
  243.   Repeat              ; find next unacknowledged message in array
  244.  
  245.     last_message_number+1
  246.     If last_message_number=101 Then last_message_number=0
  247.  
  248.     If last_message_number=free_message  ; if have got to free_message then there are no messages waiting to be acknowledged
  249.       If last_message_number=0
  250.         last_message_number=100
  251.         exit=1
  252.       Else
  253.         last_message_number-1
  254.         exit=1
  255.       EndIf
  256.     Else
  257.       If messages(last_message_number)\ack=False   ; we've found the next unacknowledged message in the array
  258.         exit=1
  259.       EndIf
  260.     EndIf
  261.  
  262.   Until exit=1
  263. End Statement
  264.  
  265. .Acknowledge_Packet   ; mark message as been sucessfully sent
  266. Function Acknowledge_Packet{ack_packet_number,player}
  267.  
  268.   SHARED last_message_number,free_message,messages(),pingprintx()
  269.   SHARED packet_number.l
  270.  
  271.   a=last_message_number
  272.   exit=-2
  273.   Repeat
  274.     If messages(a)\number=ack_packet_number   ; find message in sent messages array
  275.       messages(a)\ack=True         ; note it as being received
  276.       Format "0000"
  277.       current_time.l=Ticks
  278.       current_lag=current_time-messages(a)\timestamp ; calculate lag
  279.       b$=Str$(current_lag)
  280.       WLocate pingprintx(player),32
  281.       Print b$         ; print lag onscreen
  282.       Format""
  283.       exit=True
  284.     Else
  285.       If a=free_message  ; in case bad packet number sends it into a loop! It will only check around buffer once.
  286.         exit=False
  287.       EndIf
  288.       a+1
  289.     EndIf
  290.  
  291.     If a=101 Then a=0    ; if at end of array, jump to beginning
  292.   Until exit>-2
  293.  
  294.   If exit=True     ; only do if packet has valid packet_number
  295.     If a=last_message_number OR (a=0 AND last_message_number=100)   ; if last message unacknowledged is acknowledged
  296.       Find_Next_Message{}                                             ; then find next unacknowledged message
  297.     Else                ; we have lost a packet- resend last packet number!
  298.       If messages(last_message_number)\timestamp-current_time > current_lag+5    ; if its behind the current lag resend it
  299.         Print_String{"Received packet ACK out of order, resending last unacknowledged packet."}
  300.         If Resend_Message{last_message_number}= False   ; if run out of resends- time out message
  301.           messages(last_message_number)\timestamp=current_time+500  ; stop it resending the packet infinitely
  302.           b= Acknowledge_Packet{last_message_number,messages(last_message_number)\player}  ; wipe message off message ackno
  303.         EndIf
  304.       EndIf
  305.     EndIf
  306.   EndIf
  307.   Function Return exit
  308. End Function
  309.  
  310. .Requested_Connection      ; a player is trying to log in to us
  311. Function.s Requested_Connection{incoming_string.s}
  312.   SHARED sock,players(),host(),hostlen(),GAME_NAME.s
  313.   SHARED temphost,temphostlen,online,my_number,pingprintx()
  314.   SHARED CP_REP_ACCEPT.s,CP_REP_REJECT.s,CP_REP_PLAYER_INFO.s
  315.  
  316.   If online<2
  317.     dummy=7
  318.     Repeat            ; build game name string from incoming request
  319.       b$=Mid$(incoming_string,dummy,1)
  320.       If b$<>Chr$(0)
  321.         c$=c$+b$
  322.       EndIf
  323.       dummy+1
  324.     Until b$=Chr$(0) OR dummy=(Len(incoming_string))
  325.  
  326.     If b$=Chr$(0)
  327.       If c$=GAME_NAME.s            ; if request is for correct game
  328.         protocol=Asc(Right$(incoming_string,1))
  329.         If protocol=#NET_PROTOCOL_VERSION  ; check wether using the same protocol
  330.           a$="Connection request from "
  331.           player=1
  332.           exit=0
  333.           Repeat      ;check if a spare player slot is availiable
  334.             player+1
  335.             If players(player)\status=0
  336.               exit=1
  337.             Else
  338.               If player=#MAX_NUMBER_PLAYERS-1
  339.                 exit=1
  340.                 player=-1
  341.               EndIf
  342.             EndIf
  343.           Until exit=1
  344.  
  345.           If player>0     ; connection accepted
  346.             If Get_Host_By_Address{&temphost\sin_addr\s_addr,temphost\sin_port,player}=False
  347.               Function Return Str$(temphost\sin_addr\s_addr) + "- Host not found!"
  348.             EndIf
  349.  
  350.             players(player)\status=1
  351.             my_number=1
  352.             players(1)\status=1       ; We are now online as Server
  353.             If Get_Host_By_Address{&host(0)\sin_addr\s_addr,host(0)\sin_port,1}= False  ; put our details in Server Slot
  354.               Function Return Str$(host(0)\sin_addr\s_addr) + "- Host not found!"
  355.             EndIf
  356.  
  357.             temp$=Get_Ascii_Address{host(player)\sin_addr\s_addr}
  358.  
  359.             a$=a$+temp$ + "  Port: " + Str$(host(player)\sin_port)
  360.             players(player)\ascii_host_string=temp$   ; store it
  361.             WLocate pingprintx(1)+5,32
  362.             Print "Me"         ; print our player number as 1- the Server.
  363.  
  364.             ; build player info string to tell other players of new player
  365.             send_others$=CP_REP_PLAYER_INFO.s + Mki$(player) + Mkl$(host(player)\sin_addr\s_addr)
  366.             send_others$=send_others$ + Mki$(host(player)\sin_port) + Chr$(players(player)\player_colour)
  367.             send_others$=send_others$ + Mkl$(players(player)\score)
  368.             send_others$=send_others$ + players(player)\name + Chr$(0) + players(player)\nick
  369.  
  370.             For i=1 To #MAX_NUMBER_PLAYERS
  371.               If i<> player AND players(i)\status=1
  372.                 If i>1
  373.                   Send_Reliable_Message{send_others$,i}    ; tell others players that new player is online
  374.                                                            ; and give them new players info.
  375.                 EndIf
  376.  
  377.                 send$=CP_REP_PLAYER_INFO.s + Mki$(i) + Mkl$(host(i)\sin_addr\s_addr)
  378.                 send$=send$ + Mki$(host(i)\sin_port) + Chr$(players(i)\player_colour)
  379.                 send$=send$ + Mkl$(players(i)\score)
  380.                 send$=send$ + players(i)\name + Chr$(0) + players(i)\nick
  381.  
  382.                 Send_Reliable_Message{send$,player}    ; tell new player of others online
  383.                                                        ; and give them their info.
  384.               EndIf
  385.  
  386.             Next
  387.  
  388.             send$=CP_REP_ACCEPT.s
  389.             send$=send$ + Mki$(host(0)\sin_port)+ Mki$(player) + Mki$(map)  ; send connection accepted back to client
  390.             online=1
  391.             Send_Reliable_Message{send$,player}
  392.             Function Return a$
  393.           Else
  394.             a$=a$+"- no spare player slot!"
  395.           EndIf
  396.         Else
  397.           a$="Connection request- Incorrect Protocol: "+Str$(protocol)
  398.         EndIf
  399.       Else
  400.         a$="Wrong game name connection request: "+b$
  401.       EndIf
  402.     Else
  403.       a$="Connection request- Unknown error!"
  404.     EndIf
  405.   Else
  406.     a$="Connection Request rejected- host already logged on as Client"
  407.   EndIf
  408.  
  409.   send$=CP_REP_REJECT.s + a$    ; send connection rejected back with reason
  410.   Send_Reliable_Message{send$,0}  ; reply to sender
  411.   Function Return a$
  412.  
  413. End Function
  414.  
  415. .Decode_Packet      ; Decode incoming packet and act accordingly
  416. Function Decode_Packet{incoming_string.s}
  417.   SHARED players(),host(),temphost,online
  418.   SHARED my_number,pingprintx(),player_honesty(),game_closing
  419.   SHARED CP_REQ_PLAYER_DISCONNECT.s,CP_END_GAME_REC.s,REL_PACKET_ACK.s,CP_REP_ACCEPT.s
  420.   SHARED CP_GAME_END.s,CP_REP_PLAYER_DISCONNECTED.s
  421.  
  422.   packet_type=Asc(Mid$(incoming_string,5))
  423.   unknown_packet=0   ; check to see if valid packet is received
  424.   player=Get_Packet_Source{}  ; check to see if packet is from reliable online source
  425.   incoming_packet_number.l=Cvl(Left$(incoming_string,4))
  426.   return_message=0
  427.   exit=False
  428.   player_known=True
  429.  
  430.   Select packet_type         ; as set in `Net_Protocol_Header.bb2'
  431.     Case  #CONTROL_PACKET    ; connection or control information
  432.       packet_type=Asc(Mid$(incoming_string,6))
  433.       Select packet_type
  434.         Case #CP_REQ_CONNECT      ; request to login to Server
  435.           a$=Requested_Connection{incoming_string}
  436.           player_known=False
  437.         Case #CP_REP_ACCEPT       ; We Received confirmation of logon from Server
  438.           If online=0
  439.             online=2
  440.             my_number=Cvi(Mid$(incoming_string,9,2))
  441.             map.w=Cvi(Mid$(incoming_string,10,2))
  442.             players(1)\status=1
  443.             a$="Connection Accepted"
  444.             WLocate pingprintx(my_number)+5,32
  445.             Print "Me"      ; print our player number - as a Client.
  446.             return_message=3
  447.           Else
  448.             a$="Illegal Reply Accept attempt by player: " + Str$(player)
  449.           EndIf
  450.           player_known=False
  451.  
  452.         Case #CP_REP_REJECT       ; Our logon request was rejected by host
  453.           If online=0
  454.             a$=Mid$(incoming_string,7)
  455.             return_message=3
  456.           Else
  457.             a$="Illegal Reply Reject attempt by player: " + Str$(player)
  458.           EndIf
  459.           player_known=False
  460.  
  461.         Case #CP_REP_PLAYER_INFO  ; Information about new player that has just logged on
  462.         ;  If online=2    ; if we are a client
  463.             newplayer=Cvi(Mid$(incoming_string,7,2))
  464.             address.l=Cvl(Mid$(incoming_string,9,4))
  465.             If Get_Host_By_Address{&address,Cvi(Mid$(incoming_string,13,2)),newplayer}=True
  466.               ascii_address.s=Get_Ascii_Address{address}
  467.               players(newplayer)\status=1,ascii_address.s
  468.               players(newplayer)\player_colour=Val(Mid$(incoming_string,15,1))
  469.               players(newplayer)\score=Cvi(Mid$(incoming_string,16,4))
  470.               a$="New player at- " + ascii_address.s + " Port: " + Str$(host(newplayer)\sin_port)
  471.             Else
  472.               a$="New player at unknown host!"
  473.             EndIf
  474.             return_message=3
  475.  
  476.             player_known=False
  477.         ;  Else
  478.         ;    a$="Illegal Player Info Reply from player: " + Str$(player)
  479.         ;  EndIf
  480.  
  481.         Case #CP_REQ_PLAYER_DISCONNECT  ; Request from client to disconnect
  482.                ; or inform Server of disconnected player
  483.              If online= 1  AND player>0 ; only if Server, and from legal player
  484.                disconnecting_player=Cvi(Mid$(incoming_string,7,2))
  485.                If player=disconnecting_player  ; if player is leaving game
  486.                  dummy=0
  487.                  Print_String{"Player " +  Str$(disconnecting_player) + " has disconnected"}
  488.                  For i=2 To #MAX_NUMBER_PLAYERS
  489.                    If players(i)\status=1      ; send to all players if they're online
  490.                      If i <> disconnecting_player ; check to see if any players are left online
  491.                        dummy=1
  492.                      EndIf
  493.                      send$=CP_REP_PLAYER_DISCONNECTED.s + Mki$(disconnecting_player) ; tell other players
  494.                      Send_Reliable_Message{send$,i}   ; Send string to connected players                that player is offline
  495.                    EndIf
  496.                  Next
  497.                  players(disconnecting_player)\status=0      ; player is now offline
  498.                  If dummy=0     ; if no players are left online
  499.                    online=0
  500.                    Print_String{"All players have disconnected"}
  501.                  EndIf
  502.  
  503.                Else    ; Ping suspect player to see if they're still online
  504.  
  505.  
  506.                  player_honesty(player,disconnecting_player)=1  ; remember who is claiming player is disconnected,
  507.                                       ; so we can disconnect them if the claim is false!
  508.                  CNIF #DEBUG=1
  509.                     a$="Player " + Str$(player) + " claims player " + Str$(disconnecting_player) + " has disconnected"
  510.                  CEND
  511.                  return_message=3
  512.                EndIf
  513.              Else
  514.                a$="Illegal Player Disconnect Request Packet from player: " + Str$(player)
  515.              EndIf
  516.  
  517.         Case #CP_REP_PLAYER_DISCONNECTED    ; Server has told us a player has disconnected from the game- this can be us as well
  518.           If online=2 AND player>0
  519.             disconnected_player=Cvi(Mid$(incoming_string,7,2))
  520.             players(disconnected_player)\status=0      ; player is now offline
  521.             If disconnected_player=my_number ; my disconnection request has been confirmed by Server
  522.               online=0          ; go offline
  523.               a$="We are disconnected from the game."
  524.             Else
  525.               a$="Player " + Str$(disconnected_player) + " is disconnected from the game."
  526.             EndIf
  527.             return_message=3
  528.           Else
  529.             a$="Illegal Player Disconnected Reply Packet from player: " + Str$(player)
  530.           EndIf
  531.  
  532.         Case #CP_END_GAME   ; Server has ended Game
  533.           If player=1  ; if message is from Server
  534.             send$=CP_END_GAME_REC.s
  535.             Send_Reliable_Message{send$,1}
  536.             online=0
  537.             game_closing=500
  538.             return_message=3
  539.             a$="Server has shut down game."
  540.           Else
  541.             a$="Illegal End Game attempt by player: " + Str$(player)
  542.           EndIf
  543.  
  544.         Case #CP_END_GAME_REC  ; player has confirmed they recieved the Game End packet
  545.           If online=1 AND player>0
  546.             players(player)\status=0      ; player is now offline
  547.             dummy=0
  548.             For i=2 To #MAX_NUMBER_PLAYERS
  549.               If players(i)\status=1      ; send to all players if they're online
  550.                 dummy=1
  551.               EndIf
  552.             Next
  553.             If dummy=0   ; if all players are offline
  554.               online=0
  555.               Print_String{"Player " + Str$(player) + " has confirmed game closure."}
  556.               a$="All players have confirmed game has shut down."
  557.             Else
  558.               a$="Player " + Str$(player) + " has confirmed game closure."
  559.             EndIf
  560.             return_message=3
  561.           Else
  562.             a$="Illegal END_GAME_REC packet recieved from: " + Str$(player)
  563.           EndIf
  564.       Default
  565.         unknown_packet=1
  566.       End Select
  567.  
  568.     Case #REL_STRING_END     ; basic string message received from a player
  569.       If player>0
  570.         a$=Str$(incoming_packet_number) + "," + Str$(player)  + ": " + Mid$(incoming_string,6,Len(incoming_string)-6)
  571.         return_message=3
  572.       EndIf
  573.  
  574.     Case #REL_PACKET_ACK     ; a message has been received by a player
  575.       If player>0
  576.         If Acknowledge_Packet{incoming_packet_number,player}=True
  577.           a$=": Packet no. " + Str$(incoming_packet_number) + " arrived at destination."
  578.         Else
  579.           a$="Warning- Invalid Packet number " + Str$(incoming_packet_number) + " received!!"
  580.         EndIf
  581.       EndIf
  582.  
  583.     Default                        ; unknown packet type received
  584.       unknown_packet=1
  585.   End Select
  586.  
  587.   If unknown_packet=1     ; if unknown packet received
  588.     If player=-1                  ; if from unknown source
  589.       temp$=": " + Get_Ascii_Address{temphost\sin_addr\s_addr} + " Port: " + Str$(temphost\sin_port)
  590.     Else                         ; from known source
  591.       temp$=" Player: " + Str$(player)
  592.     EndIf
  593.     Print_String{"Warning- Unknown Packet from" + temp$}
  594.     Function Return False
  595.   EndIf
  596.  
  597.   If player_known=True AND player=-1  ; if we get sent a legal packet from unknown player
  598.     temp$=": " + Get_Ascii_Address{temphost\sin_addr\s_addr} + " Port: " + Str$(temphost\sin_port)
  599.     Print_String{"Warning- Packet from unknown Player at" + temp$}
  600.   EndIf
  601.  
  602.   If return_message>0
  603.     If return_message=3  ; send packet acknowledgment back
  604.       send$=Mkl$(incoming_packet_number)  + REL_PACKET_ACK.s  ;
  605.     EndIf
  606.     WriteUDP{&send$,Len(send$),player}  ; return a message back if return_message = 1 or 3
  607.   EndIf
  608.  
  609.   a$="R "+a$
  610.   Print_String{a$}   ; print message in the window
  611.   Function Return exit
  612. End Function
  613.  
  614. ;****************************************
  615.  
  616. ;     ARexx code
  617. ;  It seems replyrexxmsg doesn't actually work for sending result strings
  618. ;  back, use WRITERESULT, then replymsg for when you want to send
  619. ;  something back to rexx with result
  620.  
  621. Statement Result_Reply{*msg.RexxMsg,resulterror.l,resultstring$}
  622.   *msg\rm_Result1=result1
  623.   If ((*msg\rm_Action & #RXFF_RESULT)<>0)&(result1=0)
  624.     *msg\rm_Result2=CreateArgstring_(&resultstring$,Len(resultstring$))
  625.   Else
  626.     *msg\rm_Result2=0
  627.   EndIf
  628.   ReplyMsg_ *msg
  629. End Statement
  630.  
  631. .Get_Rexx_Message
  632. Function Get_Rexx_Message{}
  633.   SHARED rexxport.l,online
  634.   rmsg.l=RexxEvent(rexxport)
  635.   If IsRexxMsg(rmsg)=1
  636.     rexxline$=GetRexxCommand(rmsg,1)
  637.     Print_String {rexxline$}
  638.     linepos=Instr(rexxline$," ")
  639.     If linepos=0
  640.       command$=rexxline$
  641.     Else
  642.       command$=Left$(rexxline$,linepos-1)
  643.     EndIf
  644.     Select command$
  645.       Case "QUIT"
  646.           ReplyRexxMsg rmsg,0,0,""
  647.           Function Return False
  648.  
  649.        Case "ISONLINE"
  650.          Select online
  651.             Case 0
  652.               Result_Reply{rmsg,0,"Offline"}
  653.             Case 1
  654.               Result_Reply{rmsg,0,"Server"}
  655.             Case 2
  656.               Result_Reply{rmsg,0,"Client"}
  657.           End Select
  658.  
  659.       Case "CONNECTTOSERVER"
  660.           hostname$=Right$(rexxline$,Len(rexxline$)-linepos)
  661.           Print_String {hostname$}
  662.           If Connect_to_Server{hostname$,#PORT} : EndIf
  663.           ScreenToFront_ Peek.l(Addr Screen(0))
  664.           WindowToFront_ Peek.l(Addr Window(0))
  665.           ReplyRexxMsg rmsg,0,0,""
  666.       Default
  667.         ReplyRexxMsg rmsg,0,0,""
  668.     End Select
  669.   EndIf
  670.   Function Return True
  671. End Function
  672.  
  673. .Exit
  674. Statement Exit{text$}
  675.   SHARED UDPmem.l,sock.l,rexxport.l,rexxmsg.l
  676.  
  677.   Print_String{text$}    ; print error (if any)
  678.   FreeMem UDPmem,$2000   ; free UDP read memory buffer
  679.  
  680.   If sock>=0      ; if we've successfully opened a socket then close it
  681.     CloseSocket_(sock)
  682.     Print_String {"Socket closed"}
  683.   EndIf
  684.  
  685.   If rexxport>0              ; if we've successfully opened an Arexx port then close it
  686.     DeleteRexxMsg rexxmsg.l
  687.     DeleteMsgPort rexxport.l
  688.     Print_String{"Rexxport closed"}
  689.   EndIf
  690.  
  691.   Print_String{"Program exiting"}
  692.  
  693.   Delay_(100)
  694.   End          ; close program
  695.  
  696. End Statement
  697.  
  698.  
  699. ;****************************************
  700.  
  701. ypos=40
  702. Gosub Init_Gui
  703. messages(free_message)\ack=True
  704.  
  705. CNIF #NO_CONNECTION=1    ; if not connected to the `net- just running locally
  706.   If Initialise_UDP {Hostname,#LOCALPORT}              ; bind socket to local port
  707.      Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
  708.      WLocate 502,4
  709.      Print "Bound to: ",port_used
  710.   Else
  711.      Exit{"Error in setting up UDP at Port "+ Str$(port_used)}
  712.   EndIf
  713.  
  714.   If Get_Host_By_Name {Hostname,#PORT,0}  :EndIf   ; Set up our host details
  715.  
  716. CELSE                   ; are connected to the net or have been since computer was booted up
  717.   If Initialise_UDP {Localhost_Name{},#LOCALPORT}              ; bind socket to local port
  718.      Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
  719.      WLocate 502,4
  720.      Print "Bound to: ",port_used
  721.   Else
  722.      Exit{"Error in setting up UDP at Port "+ Str$(port_used)}
  723.   EndIf
  724.  
  725.   If Get_Host_By_Name {Localhost_Name{},#PORT,0}   :EndIf     ; Set up our host details
  726. CEND
  727.  
  728. ResetTimer
  729. exit=False
  730. game_closing=0
  731.  
  732. ;****************************************
  733.  
  734. .Main
  735.  
  736. Repeat
  737.  
  738.     VWait   ; pause a bit to allow prog to multitask nicely.
  739.  
  740.     If ev.l=$40
  741.       Select GadgetHit
  742.         Case51           ; Try to connect to a Server
  743.           If Connect_to_Server{GTGetString(0,51), GTGetInteger(0,52)} : EndIf
  744.         Case52           ; Port to connect at above Server
  745.           If Get_Host_By_Name{GTGetString(0,51), GTGetInteger(0,52),1} : EndIf
  746.         Case53           ; put in artificial delay of 1 second for testing purposes
  747.           VWait 50
  748.         Case54           ; get name of localhost
  749.           a$=Localhost_Name {}   ;
  750.           WLocate 315+pingwidth+15,hostnamey+3
  751.           Print a$
  752.         Case63          ; put in artificial delay of 40 seconds for testing purposes- causes you to lose connection
  753.           VWait 3000    ; delay 60s
  754.         Case64         ; Send string message to connected players
  755.          If online>0
  756.              temp$=GTGetString(0,64)
  757.              For i=1 To 8
  758.                If players(i)\status=1  AND i<>my_number   ; send to player if they're online and it's not my number :)
  759.                  Print_String{"S " + Str$(packet_number) + "," + Str$(i)  + ": "+ temp$}
  760.                  send$=REL_STRING_END.s + temp$ + Chr$(0)
  761.                  Send_Reliable_Message{send$,i}   ; Send string to connected players
  762.                EndIf
  763.              Next
  764.          EndIf
  765.        End Select
  766.     EndIf
  767.  
  768.     a$=ReadUDP{}   ; get data from socket
  769.  
  770.     If a$<>""      ; if there was some data waiting
  771.        If Decode_Packet{a$}=True    ; find out what was in the packet
  772.          exit=True
  773.        EndIf
  774.     EndIf
  775.  
  776.     If messages(last_message_number)\ack=False  ; only check if there's messages that haven't been acknowledged yet.
  777.       If Ticks>messages(last_message_number)\timestamp+250  ; has it been 5 secs since the packet was sent?
  778.         If Resend_Message{last_message_number}= False   ; if run out of resends- time out message
  779.           b=Acknowledge_Packet{last_message_number,messages(last_message_number)\player}  ; wipe message off message array
  780.         EndIf
  781.       EndIf
  782.     EndIf
  783.  
  784.     If Get_Rexx_Message{}=False  ; check for any arexx messages
  785.       exit=True                  ; exit if `Quit' received
  786.     EndIf
  787.  
  788.     ev.l=Event    ; get window events
  789.  
  790.     If ev=$200    ; if close button pressed
  791.       Select online
  792.         Case 0  ; we are not connected
  793.           exit=True
  794.           Print_String{"Program Closing."}
  795.         Case 1  ; if we are Server
  796.           For i=2 To 8
  797.              If players(i)\status=1  ; send to player if they're online
  798.                send$=CP_END_GAME.s
  799.                Send_Reliable_Message{send$,i}   ; Send string to connected players
  800.              EndIf
  801.           Next
  802.           Print_String{"Telling players Game has ended."}
  803.           game_closing=1500
  804.         Case 2  ;  we are client
  805.           send$=CP_REQ_PLAYER_DISCONNECT.s + Mki$(my_number)
  806.           Send_Reliable_Message{send$,1}
  807.           Print_String{"Telling Server we are disconnecting from Game."}
  808.           game_closing=500
  809.       End Select
  810.     EndIf
  811.  
  812.     If game_closing>0   ; if game is shutting down
  813.       If online=0
  814.         exit=True  ; if all players are offline then shutdown
  815.         Print_String{"We are now Offline- Closing program."}
  816.       Else
  817.         If game_closing>=2    ; carry on countdown
  818.           game_closing-1
  819.         Else                  ; if countdown reaches 10 seconds then disconnect regardless
  820.           exit=True
  821.           Print_String{"Emergency Shutdown!- unable to disconnect from Server."}
  822.         EndIf
  823.       EndIf
  824.     EndIf
  825.  
  826. Until exit=True    ; shut down if close gadget hit
  827.  
  828. Exit{""}   ; close UDP socket and Rexxport, and free read memory buffer
  829.            ; END!
  830.  
  831. ;****************************************************************************
  832.  
  833. .Init_Gui            ; setup window and gadgets- note it's a bit hacked from Alvaro Thompson's original code
  834.     WBenchToFront_   ; and isn't proportional and completely font sensitive any more :-/
  835.     WbToScreen0
  836.     DefaultIDCMP $20|$40|$200
  837.     *scr.Screen=Peek.l(Addr Screen(0))
  838.     *myfont.TextAttr=*scr\Font
  839.     fontheight=*myfont\ta_YSize
  840.     ad1.l=*myfont\ta_Name
  841.     fontname$=Peek$(ad1)
  842.  
  843.     LoadFont 1,fontname$,fontheight,0
  844.  
  845.     portwidth=TextLength_(*scr\_RastPort,"Port:",5)+10
  846.     numberswidth=TextLength_(*scr\_RastPort,"99999",5)+14
  847.     serverwidth=TextLength_(*scr\_RastPort,"Send To:",8)+10
  848.     connectwidth=TextLength_(*scr\_RastPort,"Connect",7)+14
  849.     disconnectwidth=TextLength_(*scr\_RastPort,"Disconnect",10)+14
  850.     pingwidth=TextLength_(*scr\_RastPort,"Localhost:",11)
  851.     delaywidth=TextLength_(*scr\_RastPort,"Delay 1s",8)+10
  852.     inputwidth=TextLength_(*scr\_RastPort,"Input:",6)+10
  853.     firstperson=TextLength_(*scr\_RastPort,"1:",2)+10
  854.  
  855.     #SERVERBTN       =51
  856.     #PORTBTN         =52
  857.     #BUTTON          =53
  858.  
  859.     x=1
  860.     y=1
  861.  
  862.     x1=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+5
  863.  
  864.     If #NO_CONNECTION=0
  865.       GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,Localhost_Name {}
  866.     Else
  867.       GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,"localhost"
  868.     EndIf
  869.     GTInteger 0,#PORTBTN,x+(serverwidth*3)+portwidth+3,y,numberswidth,fontheight+4,"Port:",1,#PORT
  870.     GTButton  0,#BUTTON,345,y,delaywidth,fontheight+4,"Delay 1s",$10
  871.     GTButton  0,63,340+delaywidth+10,y,delaywidth,fontheight+4,"Delay 1m",$10
  872.     y+fontheight+5
  873.  
  874.     hostnamey=y
  875.  
  876.     GTButton  0,54,315,y,pingwidth,fontheight+4,"Localhost:",$10
  877.  
  878.     WinWidth=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+8+disconnectwidth+8+90
  879.  
  880.     y+fontheight+5
  881.  
  882.     GTButton  0,55,120,y,firstperson,fontheight+4,"1:",$10
  883.     GTButton  0,56,180,y,firstperson,fontheight+4,"2:",$10
  884.     GTButton  0,57,240,y,firstperson,fontheight+4,"3:",$10
  885.     GTButton  0,58,300,y,firstperson,fontheight+4,"4:",$10
  886.     GTButton  0,59,360,y,firstperson,fontheight+4,"5:",$10
  887.     GTButton  0,60,420,y,firstperson,fontheight+4,"6:",$10
  888.     GTButton  0,61,480,y,firstperson,fontheight+4,"7:",$10
  889.     GTButton  0,62,540,y,firstperson,fontheight+4,"8:",$10
  890.     For i= 1To8
  891.       pingprintx(i)=84+ i*60
  892.     Next
  893.     pingprinty=y
  894.  
  895.     GTString  0,64,50,215,550,fontheight+4,"Send:",1,67,""
  896.  
  897.     WinHeight=y+fontheight+5
  898.  
  899.     ;WinX=WBWidth/2-(WinWidth/2)
  900.     ;WinY=WBHeight/2-(WinHeight/2)
  901.  
  902.     WinTitle$="UDP Chat"
  903.     ;ScreenTitle$="UDP Send "+Chr$(169)+"1997."
  904.  
  905.     Window 0,0,10,WinWidth,245,$0002|$0004|$0008,WinTitle$,1,2
  906.     Use Window 0:Activate 0:AttachGTList 0,0:WTitle WinTitle$,ScreenTitle$:Menus Off
  907.  
  908.     WindowOutput0
  909.     WindowInput 0
  910.  
  911.     a$=Localhost_Name {}   ;
  912.     WLocate 315+pingwidth+15,hostnamey+3
  913.     Print a$
  914.  
  915.     WLocate 10,y+3
  916.     Print"Ping Time-VBL"
  917.  
  918.     CNIF #DEBUG=0
  919.       If FindPort_(PORT_NAME.s)
  920.           Exit{"UDP_Chat already running!"}
  921.       EndIf
  922.     CEND
  923.  
  924.     rexxport.l=CreateMsgPort(PORT_NAME.s)
  925.     If rexxport=0
  926.       Exit{"Unable to open Rexxport!"}
  927.     EndIf
  928.     rexxmsg.l=CreateRexxMsg(rexxport,"rexx",PORT_NAME.s)
  929.  
  930. Return
  931.  
  932.  
  933.